package cn.nzcong.jpress.addon.articlesync.platform;

import cn.nzcong.jpress.addon.articlesync.common.Exception.AuthException;
import cn.nzcong.jpress.addon.articlesync.common.Exception.InternalServiceException;
import cn.nzcong.jpress.addon.articlesync.common.Exception.InvalidParamException;
import cn.nzcong.jpress.addon.articlesync.enums.PlatformEnum;
import cn.nzcong.jpress.addon.articlesync.service.ConfigService;
import cn.nzcong.jpress.addon.articlesync.vo.ConfigFactory;
import cn.nzcong.jpress.addon.articlesync.vo.PlatformVerifyVo;
import cn.nzcong.jpress.addon.articlesync.vo.config.BaseConfig;
import com.jfinal.aop.Inject;
import io.jboot.aop.annotation.Bean;
import io.jpress.module.article.model.Article;
import io.jpress.module.article.service.ArticleService;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 平台基础类
 *
 * @author zecong.nian
 */
@Bean
public abstract class BasePlatform<T extends BaseConfig> {

    @Inject
    private ConfigService configService;
    @Inject
    private ArticleService articleService;

    private T config = null;

    /**
     * 获取实体类型名称
     * 子类可覆盖此方法，返回：泛型T的类名.class.getSimpleName()
     * @return
     */
    private Class<T> getConfigModuleClass() {
        Type t = getClass().getGenericSuperclass();
        // Cglib代理可能会继承子类，需要取父类名称
        if(!(t instanceof ParameterizedType)){
            t = getClass().getSuperclass().getGenericSuperclass();
        }
        ParameterizedType p = (ParameterizedType) t ;
        Class<T> c = (Class<T>) p.getActualTypeArguments()[0];
        return c;
    }

    /**
     * 子类通过这个方法设定支持平台的名称
     *
     * @return
     */
    public abstract PlatformEnum getPlatformEnum();

    /**
     * 子类实现这个方法用于验证保存的账号是否有效
     *
     * @return
     */
    public abstract PlatformVerifyVo doVerify(T confJson);

    /**
     * 子类实现这个方法发布文章（with all params）
     */
    protected abstract void _doPublishArticle(String title, String article, String author, String tags, String categories);

    /**
     * 上传图片，返回远程地址
     *
     * @param imgLocalUrl
     * @return
     */
    protected abstract String _doUploadImg(String imgLocalUrl);

    /**
     * 发布操作
     *
     */
    private void doPublishArticle(String title, String article, String domain){
        // 查询并校验平台参数配置
        if (!doVerify(this.getConfigObj()).isValid()){
            throw new AuthException(this.getPlatformEnum().getDisplayName() + " 平台配置已失效");
        }
        _doPublishArticle(title, this.handleImg(article, domain), "", "", "");
    }

    public boolean enable(){
        BaseConfig config = this.getConfigObj();
        return config != null && config.isEnable();
    }

    /**
     * 替换文章中的地址为远程地址
     * @param articleHtml
     * @return
     */
    private String handleImg(String articleHtml, String domain){
        String rx = "<img.+?src=\\\"(.+?)\\\"";
        Map<String, String> imgMap = new HashMap<>();
        StringBuffer sb = new StringBuffer();
        Pattern p = Pattern.compile(rx);
        Matcher m = p.matcher(articleHtml);

        while (m.find()){
            String imgId = UUID.randomUUID().toString();
            imgMap.put(imgId, m.group(1));  // m.group(1) = "xxxxxxxx"
            String tag =  m.group(0);       // m.group(0) = "<img src=\"xxxxxxxx\""
            String repString = tag.replace(m.group(1), imgId);// repString = "<img src=\"uuid\""
            m.appendReplacement(sb, repString);
        }
        m.appendTail(sb);

        String afterStr = sb.toString();
        System.out.println(sb.toString());
        for(Map.Entry<String, String> entry : imgMap.entrySet()){
            // 异步上传和替换
            String localUrl = "http://" + domain + entry.getValue().split("\\?")[0];
            String remoteUrl = this._doUploadImg(localUrl);
            System.out.println("图片上传：" + localUrl + "  >>>>> " + remoteUrl);
            afterStr = afterStr.replaceFirst(entry.getKey(), remoteUrl);
        }
        return afterStr;
    }

    /**
     * 发布操作
     *
     */
    public void doPublishArticle(Long id, String domain){
        Article article = articleService.findById(id);
        if(article == null){
            throw new InvalidParamException("文章%s查无结果", String.valueOf(id));
        }
        // 调用子类发布方法进行文章发布
        this.doPublishArticle(article.getTitle(), article.getContent(), domain);
    }

    /**
     * 存储配置
     *
     * @param confObj
     */
    public void saveConf(BaseConfig confObj){
        if(confObj == null){
            throw new InvalidParamException();
        }
        try {
            configService.savePlatformConfig(this.getPlatformEnum().getName(), confObj);
            this.refreshConfig();
        } catch (Exception e) {
            throw new InternalServiceException(e, "内部错误");
        }
    }

    public BaseConfig parseConfig(String json){
        try {
            return ConfigFactory.getByString(json, getConfigModuleClass());
        } catch (Exception e) {
            throw new InvalidParamException(e, "转换平台设置失败");
        }
    }

    /**
     * 删除平台配置
     *
     */
    public void delConf(){
        configService.deletePlatformConfig(this.getPlatformEnum().getName());
        this.refreshConfig();
    }

    public void refreshConfig(){
        synchronized (this){
            this.config = null;
        }
    }

    /**
     * 查询平台配置
     * @return
     */
    public T getConfigObj(){
        if(this.config == null){
            synchronized (this){
                if(this.config == null){
                    T confObj = configService.getPlatformConfig(this.getPlatformEnum().getName(), getConfigModuleClass());
                    try {
                        this.config = confObj == null ? getConfigModuleClass().newInstance() : confObj;
                    } catch (Exception e) {
                        throw new InternalServiceException(e, "查询配置出错");
                    }
                }
            }
        }
        return this.config;
    }

    /**
     * 验证账号配置是否有效的入口
     *
     * @return
     */
    public PlatformVerifyVo verifyConf(){
        T confObj = this.getConfigObj();
        if(confObj == null){
            return PlatformVerifyVo.builder()
                    .platformName(getPlatformEnum().getName())
                    .platformDisplayName(getPlatformEnum().getDisplayName())
                    .build();
        }
        return this.doVerify(confObj);
    }

}
